/*
 *  Copyright (C) 2009  Peter Kist & Jan Ripke
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef __util_concurrent_list_h
#define __util_concurrent_list_h 1

#include <list>
#include <boost/thread/condition_variable.hpp>
#include <boost/thread/mutex.hpp>

namespace galaxy {

    namespace util {

        /** A thread safe list. All operations use a mutex to guarantee exclusive access.
        */
        template<typename T>
        class ConcurrentList
        {
            std::list<T>              concurrentList;
            boost::mutex              mtx;
            boost::condition_variable condition;

        public:

            /** Add specified item onto the list.
            * @param data The data to add
            */
            void push(T& data) {
                boost::mutex::scoped_lock lock(mtx);
                concurrentList.push_back(data);
                lock.unlock();
                condition.notify_one();
            }

            /** Test if the list has any items
            * @return true when the list is empty or false if not
            */
            bool empty() {
                boost::mutex::scoped_lock lock(mtx);
                return concurrentList.empty();
            }

            /** Clear the contents of the list. Note that if the list contains pointers, then
            * the objects pointed to are \b not deleted.
            */
            void clear () {
                boost::mutex::scoped_lock lock(mtx);
                return concurrentList.clear();
            }

            /** get an item from the list and return it. If the list is empty then return immediately
            * \param popped_value A reference provided by the caller that will contain the returned element.
            * \return true when a value is popped or false when the list was empty
            */
            bool try_pop(T& popped_value) {
                boost::mutex::scoped_lock lock(mtx);
                if(concurrentList.empty())
                {
                    return false;
                }

                popped_value=concurrentList.front();
                concurrentList.pop_front();
                return true;
            }

            /** get an item from the list and return it. If the list is empty then this method will wait until
            * a new element is pushed onto the list.
            * \param popped_value A reference provided by the caller that will contain the returned element.
            * \return true when a value is popped or false when the list was empty
            */
            void wait_and_pop(T& popped_value)
            {
                boost::mutex::scoped_lock lock(mtx);
                while(concurrentList.empty())
                {
                    condition.wait(lock);
                }

                popped_value=concurrentList.front();
                concurrentList.pop_front();
            }

            /** wake all threads that are currently waiting for new items to be pushed onto the list.
            */
            void wake_waiting_threads () {
                condition.notify_all();
            }
        };

    } // namespace util

} // namespace galaxy

#endif // __util_concurrent_list_h
